home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
AmigActive 10
/
AACD 10.iso
/
AACD
/
Online
/
SpeakFreely
/
src
/
speaker.c
< prev
next >
Wrap
C/C++ Source or Header
|
2000-05-18
|
78KB
|
2,614 lines
/*
Speak Freely for Unix
Sound Output Server
Designed and implemented in July of 1991 by John Walker.
*/
#include "speakfree.h"
#include "version.h"
static int audiok = FALSE; /* Audio initialised flag */
static int audiotime = 0; /* Audio timeout counter */
static int debugging = FALSE; /* Debugging enabled */
static int debugforce = FALSE; /* Debugging forced / prevented ? */
#ifdef HEXDUMP
static int hexdump = FALSE; /* Dump received packets in hex ? */
#endif
static int noring = FALSE; /* Disable remote ring requests */
gsm gsmh; /* GSM handle */
static char deskey[9] = ""; /* DES key, if any */
static char rtpdeskey[9] = ""; /* RTP DES key, if any */
static char vatdeskey[9] = ""; /* VAT DES key, if any */
static char ideakey[17] = ""; /* IDEA key, if any */
static char blowfish_spec = FALSE; /* Nonzero if Blowfish key specified */
#ifdef CRYPTO
static BF_KEY blowfishkey; /* Blowfish key */
#endif
static char *curotp = NULL; /* Key file, if any */
static int whichport = Internet_Port; /* Port to listen on (base address) */
static int sock; /* Data socket */
static int ssock; /* Control socket (RTP/VAT) */
static struct sockaddr_in from; /* Sending host address */
static struct sockaddr_in name; /* Address of destination host */
static int fromlen; /* Length of sending host address */
static int rll; /* Length of packet read from socket */
static int showhosts = FALSE; /* Show host names that connect */
static int hosttimeout = 180 * 1000000L; /* Consider host idle after this time */
static int jitter = 0; /* Jitter compensation delay */
static int jitterwait = 0; /* Jitter wait in progress */
static int jitteridle = 0; /* Countdown to reset jitter timer */
static int jitteridlet = 0; /* Jitter idle timeout interval */
struct jitterbuf { /* Jitter queue item */
struct jitterbuf *next;
LONG length;
char value[2];
};
static struct jitterbuf *jithead = NULL, *jittail = NULL; /* Jitter queue links */
static char *prog; /* Program name */
static FILE *record = NULL; /* Answering machine record file */
static char *pgppass = NULL; /* PGP secret key password */
static char *replyfile = NULL; /* Reply file pathname */
static char *replycmdexe = NULL;
static char *replycmd = "sfmike -t"; /* Default reply command */
static char *busysignal = "sleep 10; sfmike %s busy.au"; /* Default busy signal command */
static int busytimeout = 60 * 1000000L; /* Busy signal timeout interval */
static int dobusy = FALSE; /* Reject calls when busy ? */
#ifdef HALF_DUPLEX
static struct in_addr localhost; /* Our internet address */
static int halfDuplexMuted = FALSE; /* Muted by half-duplex transmission */
#endif
static struct sockaddr_in lookhost; /* Look who's listening host, if any */
static char *sdes = NULL; /* RTP SDES packet */
static int sdesl; /* RTP SDES packet length */
static unsigned long ssrc; /* RTP synchronisation source identifier */
static int lwltimer; /* Seconds before next LWL retransmit */
static int lwlonly = FALSE; /* LWL broadcast only ? */
static int actives = 0; /* Currently active hosts */
static int faceTransferActive = 0; /* Face transfers in progress */
#define LWL_RETRANSMIT (5 * 60 * 1000000L) /* Microseconds between LWL updates */
#define LWL_STRIKEOUT 5 /* Abandon LWL transmission to site
after this number of consecutive
failures. */
static struct in_addr lwl_sites[LWL_MAX_SITES]; /* LWL site addresses */
static long lwl_ports[LWL_MAX_SITES]; /* Ports for LWL hosts */
static int lwl_strikes[LWL_MAX_SITES];/* Consecutive failures to send LWL packet */
static int lwl_nsites = 0; /* Number of LWL sites published on */
static FILE *facefile = NULL; /* User's face image */
static int facesDisplayed = 0; /* Face viewers currently active */
static struct connection *conn = NULL;/* Chain of current connections */
/* Audio output and control device file names. */
#ifdef AUDIO_DEVICE_FILE
extern char *devAudioOutput, *devAudioControl;
#endif
#define Debug ((debugforce != 0) ? (debugforce > 0) : debugging)
#define TickTock (10 * 1000000L) /* Alarm interval in microseconds */
#define TockTock (60 * 1000000L) /* Alarm interval when no connections open */
#define JitterTock (1000000L / 4) /* Alarm interval for jitter compensation */
#ifdef AUDIO_RELEASE
#define AudioRelease (AUDIO_RELEASE * 1000000L) /* Release audio when idle this long */
#else
#define AudioRelease (20 * 1000000L) /* Release audio when idle this long */
#endif
static int timerStep = TockTock; /* Current timer step */
#define ucase(x) (islower(x) ? toupper(x) : (x))
/* GSMDECOMP -- Uncompress the contents of a sound buffer using GSM. */
static void gsmdecomp(sb)
struct soundbuf *sb;
{
gsm_signal dst[160];
int i, j, l = 0;
char *dpx = ((char *) sb->buffer.buffer_val) + sizeof(short);
static char dcb[BUFL];
short declen = ntohs(*((short *) sb->buffer.buffer_val));
/* Since some old releases forget to convert this field
from network byte order, kludge it back to sanity
if this happens. First we try swapping the bytes. If
that doesn't work, force the standard buffer length.
Note that we have to write this out, as ntohs will be
a no-op on a big-endian machine. */
if (declen <= 0 || declen > 1600) {
declen = ((declen & 0xFF) << 8) | ((declen >> 8) & 0xFF);
}
if (declen <= 0 || declen > 1600) {
declen = 1600;
}
for (i = 0; i < sb->buffer.buffer_len - sizeof(short);
i += sizeof(gsm_frame)) {
gsm_decode(gsmh, (gsm_byte *) dpx, dst);
dpx += sizeof(gsm_frame);
for (j = 0; j < 160; j++) {
dcb[l++] = audio_s2u(dst[j]);
}
}
bcopy(dcb, sb->buffer.buffer_val, declen);
sb->buffer.buffer_len = declen;
}
/* LPCDECOMP -- Uncompress the contents of a sound buffer using LPC. */
static void lpcdecomp(sb, c)
struct soundbuf *sb;
struct connection *c;
{
int i, l = 0;
char *dpx = ((char *) sb->buffer.buffer_val) + sizeof(short);
char dcb[BUFL];
short declen = ntohs(*((short *) sb->buffer.buffer_val));
if (declen <= 0 || declen > (10 * LPC_FRAME_SIZE)) {
declen = 10 * LPC_FRAME_SIZE;
}
for (i = 0; l < declen;
i += LPCRECSIZE) {
lpc_synthesize(dcb + l, (lpcparams_t *) (dpx + i), &c->lpcc);
l += LPC_FRAME_SIZE;
}
bcopy(dcb, sb->buffer.buffer_val, declen);
sb->buffer.buffer_len = declen;
}
/* LPC10DECOMP -- Uncompress the contents of a sound buffer using LPC10. */
static void lpc10decomp(sb, c)
struct soundbuf *sb;
struct connection *c;
{
int j;
char *dpx = ((char *) sb->buffer.buffer_val);
char dcb[BUFL];
j = lpc10decode(dpx, dcb, sb->buffer.buffer_len);
bcopy(dcb, sb->buffer.buffer_val, j);
sb->buffer.buffer_len = j;
}
/* ETIME -- Edit time and date for log messages. */
static char *etime()
{
struct tm *t;
time_t clock;
static char s[20];
time(&clock);
t = localtime(&clock);
sprintf(s, "%02d-%02d %02d:%02d", t->tm_mon + 1, t->tm_mday,
t->tm_hour, t->tm_min);
return s;
}
/* COMPRESSIONTYPE -- Return a string describing the type of
compression employed in this buffer. Assumes
this *is*, in fact, a sound buffer and not
an RTP packet, face data, etc. */
static char *compressionType(msg)
soundbuf *msg;
{
return ((msg->compression & (fComp2X | fCompGSM)) ==
(fComp2X | fCompGSM)) ?
"GSM+2X compressed" :
((msg->compression & (fComp2X | fCompADPCM)) ==
(fComp2X | fCompADPCM)) ?
"ADPCM+2X compressed" :
((msg->compression & (fComp2X | fCompLPC)) ==
(fComp2X | fCompLPC)) ?
"LPC+2X compressed" :
((msg->compression & (fComp2X | fCompLPC10)) ==
(fComp2X | fCompLPC10)) ?
"LPC10+2X compressed" :
((msg->compression & (fComp2X | fCompVOX)) ==
(fComp2X | fCompVOX)) ?
"VOX+2X compressed" :
((msg->compression & fCompADPCM) ? "ADPCM compressed" :
((msg->compression & fCompLPC) ? "LPC compressed" :
((msg->compression & fCompLPC10) ? "LPC10 compressed" :
((msg->compression & fComp2X) ? "2X compressed" :
((msg->compression & fCompGSM) ? "GSM compressed" :
((msg->compression & fCompVOX) ? "VOX compressed" :
"uncompressed"))))));
}
/* MAKESESSIONKEY -- Generate session key. */
static void makeSessionKey(key)
char *key;
{
int j;
struct MD5Context md5c;
char md5key[16], md5key1[16];
char s[1024];
s[0] = 0;
sprintf(s + strlen(s), "%u", getpid());
sprintf(s + strlen(s), "%u", getppid());
V getcwd(s + strlen(s), 256);
sprintf(s + strlen(s), "%u", clock());
V cuserid(s + strlen(s));
sprintf(s + strlen(s), "%u", time(NULL));
#ifdef Solaris
sysinfo(SI_HW_SERIAL, s + strlen(s), 12);
#else
sprintf(s + strlen(s), "%u", gethostid());
#endif
getdomainname(s + strlen(s), 256);
gethostname(s + strlen(s), 256);
sprintf(s + strlen(s), "%u", getuid());
sprintf(s + strlen(s), "%u", getgid());
MD5Init(&md5c);
MD5Update(&md5c, s, strlen(s));
MD5Final(md5key, &md5c);
sprintf(s + strlen(s), "%u", (time(NULL) + 65121) ^ 0x375F);
MD5Init(&md5c);
MD5Update(&md5c, s, strlen(s));
MD5Final(md5key1, &md5c);
#ifdef CRYPTO
init_idearand(md5key, md5key1, time(NULL));
#endif
for (j = 0; j < 16; j++) {
#ifdef CRYPTO
key[j] = idearand();
#else
key[j] = md5key[j] ^ md5key1[j];
#endif
}
#ifdef CRYPTO
close_idearand();
#endif
}
/* ULARM -- Wrapper for setitimer() that looks like alarm()
but accepts a time in microseconds. */
static void ularm(t)
long t;
{
struct itimerval it;
it.it_value.tv_sec = t / 1000000L;
it.it_value.tv_usec = t % 1000000L;
it.it_interval.tv_sec = it.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &it, NULL);
}
/* WINDTIMER -- Reset alarm timer to appropriate value
depending on what's going on at the moment. */
static void windtimer()
{
if (faceTransferActive > 0) {
/* There is an implicit assumption here that FaceFetchInterval
is <= JitterTock. If this is not the case, jitter compensation
may be too coarse during a face transfer. */
timerStep = FaceFetchInterval;
} else if (audiok || (actives > 0)) {
timerStep = (jitteridle > 0) ? JitterTock : TickTock;
} else {
timerStep = TockTock;
}
ularm(timerStep);
}
/* ISBUSY -- Test if a connection is active (item on connection
chain with a protocol other than PROTOCOL_UNKNOWN). */
static int isBusy()
{
struct connection *c;
c = conn;
while (c != NULL) {
if (c->con_protocol != PROTOCOL_UNKNOWN) {
return dobusy;
}
c = c->con_next;
}
return FALSE;
}
/* SENDLWLMESSAGE -- If enabled, send a message identifying us
to each selected Look Who's Listening server. */
static void sendLwlMessage(dobye)
int dobye;
{
int i, sock;
for (i = 0; i < lwl_nsites; i++) {
if (lwl_ports[i] >= 0) {
if (Debug) {
fprintf(stderr, "%s: updating LWL data at %s\n",
prog, inet_ntoa(lwl_sites[i]));
}
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("opening look who's listening socket");
sdes = NULL;
return;
}
lookhost.sin_port = htons(lwl_ports[i]);
bcopy((char *) (&lwl_sites[i]), (char *) &lookhost.sin_addr.s_addr,
sizeof lookhost.sin_addr.s_addr);
if (connect(sock, (struct sockaddr *) &(lookhost), sizeof lookhost) >= 0) {
if (dobye) {
char v[1024];
int l;
l = rtp_make_bye(v, ssrc, "Exiting sfspeaker", FALSE);
if (send(sock, v, l, 0) < 0) {
perror("sending look who's listening BYE packet");
}
/* No point in clearing strikes if we're exiting. */
} else {
if (send(sock, (char *) sdes, sdesl, 0) < 0) {
perror("sending look who's listening source ID message");
} else {
lwl_strikes[i] = 0; /* Success--clear strikes */
}
}
} else {
perror("connecting look who's listening socket");
/* If we get a connection refused reply, increment the number
of strikes against the server. If the server strikes out,
remove it from the list of server to which we send our
LWL information. */
if (errno == ECONNREFUSED) {
if ((++lwl_strikes[i]) >= LWL_STRIKEOUT) {
if (Debug) {
fprintf(stderr, "%s: abandoning LWL transmission to %s\n",
prog, inet_ntoa(lwl_sites[i]));
}
lwl_ports[i] = -1;
}
}
}
}
}
lwltimer = LWL_RETRANSMIT;
close(sock);
}
/* OBTAINAUDIO -- Attempt to obtain the audio output device. */
static void obtainaudio()
{
if (!audiok) {
if (!soundinit(O_WRONLY)) {
perror("opening audio output device");
fprintf(stderr, "A common cause of this error is a sound board or\n");
fprintf(stderr, "driver which cannot run in full duplex mode (some\n");
fprintf(stderr, "boards which are physically capable of full duplex\n");
fprintf(stderr, "have drivers which cannot run them in this mode).\n\n");
fprintf(stderr, "Try uncommenting the line:\n\n");
fprintf(stderr, " DUPLEX = -DHALF_DUPLEX\n\n");
fprintf(stderr, "in the Makefile, then \"make clean\", then \"make\".\n");
fprintf(stderr, "This will operate your board in half-duplex mode\n");
fprintf(stderr, "and avoid the conflict which probably caused the\n");
fprintf(stderr, "error opening audio output.\n");
return; /* Can't acquire sound device */
}
audiok = TRUE;
if (Debug) {
fprintf(stderr, "%s: opening audio device.\n", prog);
}
windtimer(); /* Reset timer, if necessary */
}
}
/* FLUSHJITTER -- Flush any buffers waiting in the jitter compensation
queue. */
static void flushjitter()
{
struct jitterbuf *jb;
/* If any samples are pending in the jitter queue, flush
them to the audio output. */
if ((jb = jithead) != NULL) {
jithead = jittail = NULL;
if (Debug) {
fprintf(stderr, "Flushing jitter queue.\n");
}
obtainaudio();
while (jb != NULL) {
struct jitterbuf *jo = jb;
if (Debug) {
fprintf(stderr,
"Playing %d samples from jitter queue.\n", jb->length);
}
if (audiok) {
audiotime = 0;
soundplay(jb->length, jb->value);
}
jb = jb->next;
free(jo);
}
}
}
/* RELEASE -- Signal-catching function which releases the audio
device if we haven't received anything to play in
the last minute. */
static void release()
{
struct connection *c, *l, *n;
/* Mark idle any hosts that haven't sent us anything recently. */
c = conn;
l = NULL;
actives = 0;
while (c != NULL) {
if ((c->con_timeout >= 0) && (c->con_timeout < hosttimeout)) {
c->con_timeout += timerStep;
actives++;
}
/* If the busy signal timer is running, decrement it.
Once it expires the host becomes eligible to attempt
to connect once again. In essence, the connection
is held open until the busy signal timeout expires, and
is then forced to go idle with the regular timeout
mechanism, making it eligible once again to attempt
connection. */
if (c->con_busy > 0) {
c->con_busy -= timerStep;
if (c->con_busy <= 0) {
c->con_busy = 0;
c->con_timeout = hosttimeout;
if (Debug) {
fprintf(stderr, "%s: %s %s busy signal timeout expired\n", prog, etime(), c->con_hostname);
}
}
}
n = c->con_next;
if (c->con_timeout >= hosttimeout) {
actives--; /* Oops--just went inactive */
c->con_timeout = -1; /* Mark inactive */
if (showhosts) {
fprintf(stderr, "%s: %s %s idle\n", prog, etime(), c->con_hostname);
}
c->con_protocol = PROTOCOL_UNKNOWN;
c->con_uname[0] = c->con_email[0] = 0;
c->con_rseq = -1;
/* If a face file transfer is in progress, shut it down and
discard the incomplete face file. */
if (c->face_stat == FSrequest || c->face_stat == FSreply) {
if (c->face_file != NULL) {
fclose(c->face_file);
c->face_file = NULL;
}
unlink(c->face_filename);
c->face_filename[0] = 0;
faceTransferActive--;
}
/* If there's a face viewer active, terminate it and delete
the face image file, if any. */
if (c->face_viewer > 0) {
kill(c->face_viewer, SIGHUP);
c->face_viewer = 0;
if (facesDisplayed > 0) {
facesDisplayed--;
}
unlink(c->face_filename);
c->face_filename[0] = 0;
c->face_file = NULL;
}
c->face_stat = FSinit;
/* If there's a PGP session key for this host, don't actually
release the connection buffer, as that would lose it. Issue
an idle message, though, and tweak the timeout so we do this
only once. */
if (c->pgpkey[0] == 0) {
if (l == NULL) {
conn = n;
} else {
l->con_next = n;
}
free(c);
}
} else {
int makereq = FALSE;
/* If a face file transfer is in progress, request
the next block or, if it's time, re-issue the last
request if the timeout has expired. */
if (c->face_stat == FSreply) {
makereq = TRUE;
c->face_retry = 0;
if (Debug) {
fprintf(stderr, "%s: request face data at %ld from %s\n",
prog, c->face_address, c->con_hostname);
}
} else if (c->face_stat == FSrequest) {
c->face_timeout += timerStep;
if (c->face_timeout >= FaceTimeout) {
if (Debug) {
fprintf(stderr, "%s: retry %d reissue face data request at %ld from %s\n",
prog, c->face_retry, c->face_address, c->con_hostname);
}
if (c->face_retry > FaceMaxRetries) {
if (c->face_file != NULL) {
fclose(c->face_file);
c->face_file = NULL;
}
unlink(c->face_filename);
c->face_filename[0] = 0;
c->face_stat = FSabandoned;
faceTransferActive--;
if (Debug) {
fprintf(stderr, "%s: timeout, no face image available for %s\n",
prog, c->con_hostname);
}
} else {
makereq = TRUE;
c->face_retry++;
}
}
}
if (makereq) {
sbhead fsb;
c->face_stat = FSrequest;
c->face_timeout = 0;
bcopy((char *) &(c->con_addr), (char *) &(name.sin_addr),
sizeof(struct in_addr));
fsb.compression = htonl(fProtocol | fFaceData | faceRequest);
fsb.buffer.buffer_len = htonl(c->face_address);
if (sendto(sock, (char *) &fsb,
(int) (sizeof(struct soundbuf) - BUFL),
0, (struct sockaddr *) &(name), sizeof name) < 0) {
perror("requesting face image data");
}
}
l = c;
}
c = n;
}
/* Release the sound device after two ticks. This allows
other programs to use it while we're idle. */
if (audiok && ((audiotime += timerStep) >= AudioRelease)) {
soundterm();
audiok = FALSE;
if (Debug) {
fprintf(stderr, "%s: releasing audio device.\n", prog);
}
/* Flush the record file so if we're rudely terminated it
will be up to date as of the last audio idle time. */
if (record != NULL) {
fflush(record);
}
}
/* Update our Look Who's Listening information if the
timeout has expired. */
lwltimer -= timerStep;
if (sdes != NULL && lwltimer <= 0) {
sendLwlMessage(FALSE);
}
/* If sound is waiting in the jitter queue, decrement the
jitter timer and flush the queue if it decrements to zero. */
if (jithead != NULL && jitterwait > 0) {
jitterwait -= timerStep;
if (jitterwait <= 0) {
jitterwait = 0;
flushjitter();
}
}
/* Count down the jitter idle timer. When it's zero, the
first packets of an audio stream will be buffered in
the jitter queue. */
if (jitteridle > 0) {
jitteridle -= timerStep;
if (jitteridle <= 0) {
jitteridle = 0;
if (Debug) {
fprintf(stderr, "%s: jitter idle\n", prog);
}
}
}
/* If we still own the sound device or have a connection
open, reset the timer. */
windtimer();
if (Debug) {
time_t t = time(NULL);
fprintf(stderr, "%s: Tick: %.2f... %s", prog,
timerStep / 1000000.0, ctime(&t));
}
signal(SIGALRM, release); /* Set signal to handle timeout */
}
/* EXITING -- Catch as many program termination signals as
possible and clean up before exit. */
static void exiting()
{
struct connection *c = conn;
if (sdes) {
sendLwlMessage(TRUE);
}
/* Terminate any active face viewers. */
while (c != NULL) {
if (c->face_viewer > 0) {
kill(c->face_viewer, SIGHUP);
unlink(c->face_filename);
}
c = c->con_next;
}
exit(0);
}
/* VIEWERTERM -- Handle termination of a face viewer child
process. This is necessary both to avoid
viewers the user terminates manually becoming
zombie processes, and also to delete the
face file no longer associated with a viewer. */
static void viewerterm()
{
int status, pid;
while ((pid =
#ifdef Solaris
/* Should work for any System V Unix. */
waitpid(0 /* or -1 */,&status, WNOHANG)
#else
/* BSD and IRIX implement wait3(). */
wait3(&status, WNOHANG, 0)
#endif
) > 0) {
if (Debug) {
fprintf(stderr, "Child process %d done\n", pid);
}
if (WIFEXITED(status) || WIFSIGNALED(status)) {
struct connection *c = conn;
while (c != NULL) {
if (pid == (int) c->face_viewer) {
c->face_viewer = 0;
unlink(c->face_filename);
if (Debug) {
fprintf(stderr, "Face viewer pid %d terminated. Deleting %s\n", pid, c->face_filename);
}
c->face_filename[0] = 0;
if (facesDisplayed > 0) {
facesDisplayed--;
if (facesDisplayed == 0) {
}
}
}
c = c->con_next;
}
}
}
signal(SIGCHLD, viewerterm); /* Set signal to handle face viewer termination */
}
/* PLAYBUFFER -- Send next buffer to audio output. */
static void playbuffer(msg, c)
soundbuf *msg;
struct connection *c;
{
char *val;
LONG len;
char auxbuf[BUFL + 2], bbuf[8], tbuf[8];
debugging = (msg->compression & fDebug) ? TRUE : FALSE;
audiotime = 0; /* Reset timeout counter */
obtainaudio(); /* Open audio output if necessary */
if (showhosts && (c->con_compmodes != (fCompressionModes &
msg->compression))) {
c->con_compmodes = fCompressionModes & msg->compression;
fprintf(stderr, "%s: %s sending %s.\n", prog, c->con_hostname,
compressionType(msg));
}
len = msg->buffer.buffer_len;
val = msg->buffer.buffer_val;
if (Debug) {
fprintf(stderr, "%s: playing %d %s bytes from %s.\n", prog, len,
compressionType(msg), c->con_hostname);
}
/* If the fSetDest bit is on, use the fDestJack bit to re-route
the sound. This is normally used to divert the sound to the
speaker to get an individual's attention. This can be
disabled with the -N option. */
if ((msg->compression & fSetDest) && !noring) {
sounddest((msg->compression & fDestJack) ? 1 : 0);
if (!(msg->compression & fDestJack)) {
if (audiok) {
soundplayvol(50); /* Make sure volume high enough */
}
}
}
#ifdef CRYPTO
/* If message is encrypted, decrypt. */
if ((msg->compression & fEncOTP) && (curotp != NULL)) {
int i;
LONG slen = (len + 7) & (~7);
if (Debug) {
fprintf(stderr, "%s: decrypting %d bytes with key file.\n", prog, len);
}
for (i = 0; i < slen; i ++) {
val[i] ^= curotp[i];
}
}
if ((msg->compression & fEncPGP) && c->pgpkey[0]) {
unsigned short iv[4];
LONG slen = (len + 7) & (~7);
bzero(iv, sizeof(iv));
initcfb_idea(iv, c->pgpkey + 1, TRUE);
if (Debug) {
fprintf(stderr, "%s: decrypting %d bytes with PGP key.\n", prog, slen);
}
ideacfb(val, slen);
close_idea();
}
if ((msg->compression & fEncBF) && blowfish_spec) {
unsigned char iv[8];
LONG slen = (len + 7) & (~7);
bzero(iv, sizeof(iv));
if (Debug) {
fprintf(stderr, "%s: decrypting %d bytes with Blowfish key.\n", prog, slen);
}
BF_cbc_encrypt((unsigned char *) val, (unsigned char *) val,
slen, &blowfishkey, iv, BF_DECRYPT);
}
if ((msg->compression & fEncIDEA) && ideakey[0]) {
unsigned short iv[4];
LONG slen = (len + 7) & (~7);
bzero(iv, sizeof(iv));
initcfb_idea(iv, ideakey + 1, TRUE);
if (Debug) {
fprintf(stderr, "%s: decrypting %d bytes with IDEA key.\n", prog, slen);
}
ideacfb(val, slen);
close_idea();
}
if ((msg->compression & fEncDES) && deskey[0]) {
int i;
LONG slen = (len + 7) & (~7);
setkey(deskey + 1);
if (Debug) {
fprintf(stderr, "%s: decrypting %d bytes with DES key.\n", prog, slen);
}
for (i = 0; i < slen; i += 8) {
bcopy(val + i, tbuf, 8);
dedes(val + i);
/* Reverse cipher block chaining. */
if (i > 0) {
int j;
for (j = 0; j < 8; j++) {
val[(i + j)] ^= bbuf[j];
}
}
bcopy(tbuf, bbuf, 8);
}
}
#else
if ((msg->compression & (fEncDES | fEncOTP | fEncIDEA | fEncPGP | fEncBF)) &&
!(c->con_crypt_warning)) {
c->con_crypt_warning = TRUE;
fprintf(stderr, "%s: Warning: host %s is sending encrypted audio\n",
prog, c->con_hostname);
fprintf(stderr, " which cannot be decoded by this version of\n");
fprintf(stderr, " Speak Freely, which was built with encryption\n");
fprintf(stderr, " removed to permit redistribution without concern\n");
fprintf(stderr, " for export control and other regulations.\n");
fprintf(stderr, " You can download a version of Speak Freely which\n");
fprintf(stderr, " includes full encryption from:\n");
fprintf(stderr, " http://www.fourmilab.ch/speakfree/unix/\n");
}
#endif
/* If message is compressed, decompress appropriately. */
if (msg->compression & fCompVOX) {
vox_gsmdecomp(msg);
len = msg->buffer.buffer_len;
}
if (msg->compression & fCompGSM) {
gsmdecomp(msg);
len = msg->buffer.buffer_len;
}
if (msg->compression & fCompADPCM) {
adpcmdecomp(msg);
len = msg->buffer.buffer_len;
}
if (msg->compression & fCompLPC) {
lpcdecomp(msg, c);
len = msg->buffer.buffer_len;
}
if (msg->compression & fCompLPC10) {
lpc10decomp(msg, c);
len = msg->buffer.buffer_len;
}
if (msg->compression & fComp2X) {
int is = len, os = len * 2;
rate_flow(val, auxbuf, &is, &os);
len = os;
val = auxbuf;
}
/* If the jitter idle time has elapsed, reset the jitter
wait to queue the start of this packet sequence. */
if (jitter > 0 && jitteridle == 0 && jitterwait == 0) {
jitterwait = jitter;
if (Debug) {
fprintf(stderr, "%s: starting %ld millisecond jitter delay.\n", prog, jitterwait / 1000L);
}
}
/* Receipt of any sound buffer resets the jitter idle timer. */
jitteridle = jitteridlet; /* Reset jitter idle countdown */
/* If the jitter wait has not yet expired, add the samples to
the queue waiting to be played at the end of the jitter
wait. */
if (jitterwait > 0) {
struct jitterbuf *jb;
jb = (struct jitterbuf *) malloc(sizeof(struct jitterbuf) + len);
if (jb != NULL) {
bcopy(val, jb->value, len);
jb->length = len;
jb->next = NULL;
if (jittail != NULL) {
jittail->next = jb;
}
jittail = jb;
if (jithead == NULL) {
jithead = jb;
windtimer();
}
if (Debug) {
fprintf(stderr, "%s: adding %d samples to jitter queue.\n",
prog, len);
}
}
} else {
flushjitter();
if (audiok) {
soundplay(len, val);
}
}
if (record != NULL) {
fwrite(val, len, 1, record);
}
}
/* PROGNAME -- Extract program name from argv[0]. */
static char *progname(arg)
char *arg;
{
char *cp = strrchr(arg, '/');
return (cp != NULL) ? cp + 1 : arg;
}
/* USAGE -- Print how-to-call information. */
static void usage()
{
V fprintf(stderr, "%s -- Speak Freely sound receiver.\n", prog);
V fprintf(stderr, " %s.\n", Relno);
V fprintf(stderr, "\n");
V fprintf(stderr, "Usage: %s [options]\n", prog);
V fprintf(stderr, "Options:\n");
V fprintf(stderr, " -A\"file command\" Create answer command in file\n");
V fprintf(stderr, " -B\"command\" Execute command for busy signal\n");
#ifdef CRYPTO
V fprintf(stderr, " -BFkey Blowfish decrypt with key\n");
#endif
V fprintf(stderr, " -D Force debug output\n");
V fprintf(stderr, " -E\"command\" Execute command on incoming call\n");
#ifdef CRYPTO
V fprintf(stderr, " -Ikey IDEA decrypt with key\n");
#endif
V fprintf(stderr, " -Jwait,idle Jitter delay wait and idle in milliseconds\n");
#ifdef CRYPTO
V fprintf(stderr, " -Kkey DES decrypt with key\n");
#endif
#ifdef MULTICAST
V fprintf(stderr, " -Mhost/ip Join multicast to given name or IP address\n");
#endif
V fprintf(stderr, " -N Disable remote ring requests\n");
#ifdef CRYPTO
V fprintf(stderr, " -Ofile Use file as key file\n");
#endif
V fprintf(stderr, " -Pport Listen on given port\n");
V fprintf(stderr, " -Q Prevent debug output\n");
V fprintf(stderr, " -R[+]file Record [append] sound in file\n");
V fprintf(stderr, " -U Print this message\n");
V fprintf(stderr, " -Vtimeout Show hostnames that connect\n");
V fprintf(stderr, " -W Publish LWL information for sfvod\n");
#ifdef HEXDUMP
V fprintf(stderr, " -X Dump packets in hex\n");
#endif
#ifdef AUDIO_DEVICE_FILE
V fprintf(stderr, " -Youtdev[:ctldev] Override default audio device file name or specify open #fd\n");
#endif
#ifdef CRYPTO
V fprintf(stderr, " -Z\"phrase\" Set PGP secret key pass phrase\n");
#endif
V fprintf(stderr, "\n");
V fprintf(stderr, "by John Walker\n");
V fprintf(stderr, " http://www.fourmilab.ch/\n");
#ifndef CRYPTO
V fprintf(stderr, "\n");
V fprintf(stderr, "Note: This version of Speak Freely was built with\n");
V fprintf(stderr, " encryption code removed to permit redistribution\n");
V fprintf(stderr, " without concern for export control and regulations\n");
V fprintf(stderr, " concerning encryption technology in some jurisdictions.\n");
V fprintf(stderr, " You can download a version of Speak Freely with\n");
V fprintf(stderr, " full encryption from:\n");
V fprintf(stderr, " http://www.fourmilab.ch/speakfree/unix/\n");
#endif
}
/* Main program. */
main(argc, argv)
int argc;
char *argv[];
{
int i, j, k, l, length;
struct soundbuf sb;
FILE *fp;
struct connection *c;
char *cp;
int newconn, wasrtp;
struct auhdr { /* .au file header */
char magic[4];
long hsize, dsize, emode, rate, nchan;
};
static struct auhdr afh = { ".snd", sizeof(struct auhdr), 1, 1, 8000, 1 };
prog = progname(argv[0]);
/* First pass option processing. We have to first scan
the options to handle any which affect creation of the
socket. On the second pass we can assume the socket
already exists, allowing us to join multicast groups,
etc. */
for (i = 1; i < argc; i++) {
char *op, opt;
op = argv[i];
if (*op == '-') {
opt = *(++op);
if (islower(opt)) {
opt = toupper(opt);
}
switch (opt) {
case 'D': /* -D -- Force debug output */
debugforce = TRUE;
break;
case 'P': /* -Pport -- Port to listen on */
if (op[1] != 0) {
whichport = atoi(op + 1);
}
break;
case 'U': /* -U -- Print usage information */
case '?': /* -? -- Print usage information */
usage();
return 0;
case 'W': /* -Wport -- Publish LWL information for sfvod */
lwlonly = TRUE;
if (op[1] != 0) {
whichport = atoi(op + 1);
}
break;
}
} else {
usage();
return 2;
}
}
if (!lwlonly) {
/* Create the sockets from which to read */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("opening data socket");
return 1;
}
ssock = socket(AF_INET, SOCK_DGRAM, 0);
if (ssock < 0) {
perror("opening control socket");
return 1;
}
/* Create name with wildcards. */
name.sin_family = AF_INET;
name.sin_addr.s_addr = INADDR_ANY;
name.sin_port = htons(whichport);
if (bind(sock, (struct sockaddr *) &name, sizeof name) < 0) {
perror("binding data socket");
return 1;
}
name.sin_port = htons(whichport + 1);
if (bind(ssock, (struct sockaddr *) &name, sizeof name) < 0) {
perror("binding control socket");
return 1;
}
name.sin_port = htons(whichport);
/* Process command line options. */
pgppass = getenv("PGPPASS");
#if Internet_Port != 2074
/* This is a reminder to the author who frequently tests with
non-standard ports to avoid inadvertently releasing a
production version configured for the wrong port. */
fprintf(stderr, "%s: warning, listening on non-standard Internet port %d\n",
prog, Internet_Port);
#endif
for (i = 1; i < argc; i++) {
char *op, opt;
op = argv[i];
if (*op == '-') {
opt = *(++op);
if (islower(opt)) {
opt = toupper(opt);
}
switch (opt) {
case 'A': /* -A"file command" -- Create reply command on connect */
{
char *cp = strchr(op + 1, ' ');
if (cp != NULL) {
replycmd = cp + 1;
*cp = 0;
}
replyfile = op + 1;
}
break;
case 'E': /* -E"command" -- Execute command on connect */
replycmdexe = op + 1;
break;
case 'B': /* -B"command" -- Execute command for busy signal. */
#ifdef CRYPTO
/* -BFkey -- Set Blowfish key */
if (ucase(op[1]) == 'F') {
if (strlen(op + 2) == 0) {
blowfish_spec = FALSE;
} else {
struct MD5Context md5c;
unsigned char bfvec[16];
blowfish_spec = TRUE;
MD5Init(&md5c);
MD5Update(&md5c, op + 2, strlen(op + 2));
MD5Final(bfvec, &md5c);
BF_set_key(&blowfishkey, 16, bfvec);
if (Debug) {
fprintf(stderr, "Blowfish key:");
for (j = 0; j < 16; j++) {
fprintf(stderr, " %02X", (bfvec[j] & 0xFF));
}
fprintf(stderr, "\n");
}
}
} else
#endif
{
if (strlen(op + 1) > 0) {
if (!strstr(op + 1, "%s")) {
fprintf(stderr,
"%s: invalid -B option command: must contain %%s for IP address insertion.\n",
prog);
} else {
busysignal = op + 1;
dobusy = TRUE;
}
} else {
dobusy = TRUE;
}
}
break;
#ifdef CRYPTO
case 'I': /* -Ikey -- Set IDEA key */
if (strlen(op + 1) == 0) {
ideakey[0] = FALSE;
} else {
struct MD5Context md5c;
MD5Init(&md5c);
MD5Update(&md5c, op + 1, strlen(op + 1));
MD5Final(ideakey + 1, &md5c);
ideakey[0] = TRUE;
if (Debug) {
fprintf(stderr, "IDEA key:");
for (j = 0; j < 16; j++) {
fprintf(stderr, " %02X", (ideakey[j + 1] & 0xFF));
}
fprintf(stderr, "\n");
}
}
break;
#endif
case 'J': /* -Jwait,idle -- Set jitter wait and idle times */
if (isdigit(*(op + 1))) {
char *cp = strchr(op + 1, ',');
if (cp != NULL) {
*cp = 0;
}
jitter = atoi(op + 1) * 1000L;
jitteridlet = (cp == NULL) ? (jitter * 2) :
(atoi(cp + 1) * 1000L);
if (jitter < 250000) {
fprintf(stderr,
"%s: invalid jitter wait %d--reset to 250 milliseconds.\n",
prog, jitter / 1000);
jitter = 250000;
}
if (jitteridlet < 250000) {
fprintf(stderr,
"%s: invalid jitter idle time %d--reset to 250 milliseconds.\n",
prog, jitteridlet / 1000);
jitteridlet = 250000;
}
} else {
jitteridlet = 2 * (jitter = 1000000L);
}
break;
#ifdef CRYPTO
case 'K': /* -Kkey -- Set DES key */
desinit(1); /* Initialise the DES library */
if (strlen(op + 1) == 0) {
deskey[0] = rtpdeskey[0] = vatdeskey[0] = FALSE;
} else {
struct MD5Context md5c;
char md5key[16], algorithm[16];
MD5Init(&md5c);
MD5Update(&md5c, op + 1, strlen(op + 1));
MD5Final(md5key, &md5c);
for (j = 0; j < 8; j++) {
deskey[j + 1] = (char)
((md5key[j] ^ md5key[j + 8]) & 0x7F);
}
deskey[0] = TRUE;
des_string_to_key(op + 1, (des_cblock *) (vatdeskey + 1));
string_DES_key(op + 1, rtpdeskey + 1, algorithm);
if (strcmp(algorithm, "DES-CBC") != 0) {
fprintf(stderr, "Unsupported encryption algorithm: %s. Only DES-CBC is available.\n",
algorithm);
return 2;
}
rtpdeskey[0] = vatdeskey[0] = TRUE;
if (Debug) {
fprintf(stderr, "DES key:");
for (j = 0; j < 8; j++) {
fprintf(stderr, " %02X", (deskey[j + 1] & 0xFF));
}
fprintf(stderr, "\n");
fprintf(stderr, "RTP key:");
for (j = 0; j < 8; j++) {
fprintf(stderr, " %02X", (rtpdeskey[j + 1] & 0xFF));
}
fprintf(stderr, "\n");
fprintf(stderr, "VAT key:");
for (j = 0; j < 8; j++) {
fprintf(stderr, " %02X", (vatdeskey[j + 1] & 0xFF));
}
fprintf(stderr, "\n");
}
}
break;
#endif
#ifdef MULTICAST
case 'M': /* -Mhost/ip -- Join multicast to name/IP number */
{
struct ip_mreq m;
struct hostent *h;
long naddr;
/* If it's a valid IP number, use it. Otherwise try to look
up as a host name. */
if ((naddr = inet_addr(op + 1)) == -1) {
h = gethostbyname(op + 1);
if (h == 0) {
fprintf(stderr, "%s: unknown multicast group\n", op + 1);
return 2;
}
bcopy((char *) h->h_addr, (char *) &naddr, sizeof naddr);
}
m.imr_multiaddr.s_addr = naddr;
m.imr_interface.s_addr = htons(INADDR_ANY);
if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP,
(char *) &m, sizeof m) < 0) {
perror("joining multicast group");
return 2;
}
}
break;
#endif
case 'N': /* -N -- Disable remote ring requests */
noring = TRUE;
break;
#ifdef CRYPTO
case 'O': /* -Ofile -- Use file as key file */
fp = fopen(op + 1, "r");
if (fp == NULL) {
perror("Cannot open key file");
return 2;
}
curotp = malloc(BUFL);
if (curotp == NULL) {
fprintf(stderr, "%s: Cannot allocate key file buffer.\n", prog);
return 2;
}
l = fread(curotp, 1, BUFL, fp);
if (l == 0) {
/* Idiot supplied void key file. Give 'im
what he asked for: no encryption. */
curotp[0] = 0;
l = 1;
}
fclose(fp);
/* If the file is shorter than the maximum buffer
we may need to encrypt, replicate the key until
the buffer is filled. */
j = l;
k = 0;
while (j < BUFL) {
curotp[j++] = curotp[k++];
if (k >= l) {
k = 0;
}
}
break;
#endif
case 'Q': /* -Q -- Prevent debug output */
debugforce = -1;
break;
case 'R': /* -Rfile -- Answering machine record */
{
char *fn = op + 1;
int append = FALSE;
if (*fn == '+') {
fn++;
append = TRUE;
}
record = fopen(fn, append ? "a" : "w");
if (record == NULL) {
perror("Cannot create answering machine file");
return 2;
}
fseek(record, 0L, 2);
if (ftell(record) == 0) {
fwrite(&afh, sizeof afh, 1, record);
}
fflush(record);
}
break;
case 'V': /* -Vtimeout -- Show hostnames that connect */
showhosts = TRUE;
if (op[1] != 0) {
int t = atoi(op + 1) * 1000000L;
if (t > 0) {
if (t < (TickTock + 1)) {
t = TickTock + 1;
}
hosttimeout = (t / TickTock) * TickTock;
}
}
break;
#ifdef HEXDUMP
case 'X': /* -X -- Dump packets in hex */
hexdump = debugforce = TRUE;
break;
#endif
#ifdef AUDIO_DEVICE_FILE
case 'Y': /* -Youtdev:[ctldev] -- Specify audio
output and control device
file names or #open_fd. */
devAudioOutput = op + 1;
if (strchr(op + 1, ':') != NULL) {
devAudioControl = strchr(op + 1, ':') + 1;
}
break;
#endif
#ifdef CRYPTO
case 'Z': /* -Z"phrase" -- Set PGP pass phrase */
if (op[1] != 0) {
pgppass = strdup(op + 1);
op = argv[i];
/* Zap the pass phrase in memory so "ps" etc. don't see it. */
while (*op != 0) {
*op++ = ' ';
}
} else {
char s[256];
fprintf(stderr, "PGP secret key pass phrase: ");
fflush(stderr);
initscr();
noecho();
getstr(s);
pgppass = strdup(s);
echo();
endwin();
fprintf(stderr, "\n");
}
break;
#endif
}
}
}
#ifdef HALF_DUPLEX
{
#ifndef INADDR_LOOPBACK
/* The standard local host loopback address is supposed to be
defined in <netinet/in.h>, but just in case some screwball
system doesn't, let's include our own definition here. */
#define INADDR_LOOPBACK 0x7F000001
#endif
localhost.s_addr = htonl(INADDR_LOOPBACK);
#ifdef HDX_DEBUG
if (Debug) {
fprintf(stderr, "%s: local host %s\n", prog,
inet_ntoa(localhost));
}
#endif
}
#endif
#ifdef LINUX_FPU_FIX
__setfpucw(_FPU_IEEE); /* Mask floating point interrupts to
enable standard IEEE processing.
Current libc releases do this by
default, so this is needed only on
older libraries. */
#endif
rate_start(4000, 8000);
/* Initialise GSM decoding. */
gsmh = gsm_create();
/* Initialise LPC decoding. */
if (!lpc_start()) {
fprintf(stderr, "Cannot allocate LPC decoding memory.\n");
return 1;
}
lpc10init();
/* Find assigned port value and print it. */
length = sizeof(name);
if (getsockname(sock, (struct sockaddr *) &name, &length) < 0) {
perror("getting socket name");
return 1;
}
#ifdef SHOW_SOCKET
fprintf(stderr, "%s: socket port #%d\n", prog, ntohs(name.sin_port));
#endif
}
/* Contact look who's listening host, if requested. */
cp = getenv("SPEAKFREE_LWL_TELL");
if (cp != NULL) {
struct hostent *h;
char md5key[16];
char *ep, *np;
long iadr;
int n;
makeSessionKey(md5key);
bcopy(md5key, (char *) &ssrc, sizeof ssrc);
sdesl = rtp_make_sdes(&sdes, ssrc, whichport, FALSE);
lookhost.sin_family = AF_INET;
while (lwl_nsites < LWL_MAX_SITES) {
n = lwl_nsites;
while (*cp != 0 && isspace(*cp)) {
cp++;
}
if (*cp == 0) {
break;
}
if ((np = strchr(cp, ',')) != NULL) {
*np++ = 0;
}
lwl_ports[lwl_nsites] = Internet_Port + 2;
lwl_strikes[lwl_nsites] = 0; /* Clear strikeout counter */
if ((ep = strchr(cp, ':')) != NULL) {
*ep = 0;
lwl_ports[lwl_nsites] = atoi(ep + 1);
}
if (isdigit(*cp) && (iadr = inet_addr(cp)) != -1) {
bcopy((char *) &iadr, (char *) (&lwl_sites[lwl_nsites]),
sizeof iadr);
lwl_nsites++;
} else {
h = gethostbyname(cp);
if (h != NULL) {
bcopy((char *) (h->h_addr),
(char *) (&lwl_sites[lwl_nsites]),
sizeof(unsigned long));
lwl_nsites++;
} else {
fprintf(stderr, "%s: warning, SPEAKFREE_LWL_TELL host %s unknown.\n",
prog, cp);
}
}
if (Debug && lwl_nsites > n) {
fprintf(stderr, "%s: publishing on LWL server %s: %s.\n", prog,
inet_ntoa(lwl_sites[n]), cp);
}
if (np == NULL) {
break;
}
cp = np;
}
if (lwl_nsites > 0) {
sendLwlMessage(FALSE);
}
}
/* See if the user has specified a face file. If so,
try to open it. */
if (!lwlonly) {
char *cp = getenv("SPEAKFREE_FACE");
if (cp != NULL) {
if ((facefile = fopen(cp, "r")) == NULL) {
fprintf(stderr, "%s: cannot open SPEAKFREE_FACE file %s\n",
prog, cp);
}
}
}
signal(SIGHUP, exiting); /* Set signal to handle termination */
signal(SIGINT, exiting); /* Set signal to handle termination */
signal(SIGTERM, exiting); /* Set signal to handle termination */
signal(SIGALRM, release); /* Set signal to handle timeout */
signal(SIGCHLD, viewerterm); /* Set signal to handle face viewer termination */
windtimer(); /* Set periodic status update */
/* If all we're doing is broadcasting a periodic LWL update
for sfvod, simply sit in a timer loop forever and let the
SIGALRM handler take care of business. */
while (lwlonly) {
if (lwl_nsites == 0) {
/* -W option but no valid sites found. Exit with error
status. */
return 2;
}
pause();
}
/* Read from the socket. */
while (TRUE) {
fd_set fdset;
int wsock, control, busyreject;
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
FD_SET(ssock, &fdset);
if (select(FD_SETSIZE, &fdset, NULL, NULL, NULL) <= 0) {
continue;
}
wsock = FD_ISSET(sock, &fdset) ? sock :
(FD_ISSET(ssock, &fdset) ? ssock : -1);
if (wsock < 0) {
continue;
}
control = wsock == ssock;
fromlen = sizeof(from);
if ((rll = recvfrom(wsock, (char *) &sb, sizeof sb, 0, (struct sockaddr *) &from, &fromlen)) < 0) {
if (errno != EINTR) {
perror(!control ? "receiving data packet" :
"receiving control packet");
}
continue;
}
#ifdef HEXDUMP
if (hexdump) {
fprintf(stderr, "%s: %d bytes read from %s socket.\n", prog, rll,
control ? "control" : "data");
xd(stderr, &sb, rll, TRUE);
}
#endif
/* See if this connection is active. If not, initialise a new
connection. */
busyreject = FALSE;
newconn = FALSE;
c = conn;
while (c != NULL) {
if (memcmp(&from.sin_addr, &(c->con_addr),
sizeof(struct in_addr)) == 0) {
break;
}
c = c->con_next;
}
if (c == NULL) {
c = (struct connection *) malloc(sizeof(struct connection));
if (c != NULL) {
struct hostent *h;
newconn = TRUE;
c->con_next = conn;
c->pgpkey[0] = FALSE;
bzero(c->keymd5, 16);
c->con_uname[0] = c->con_email[0] = 0;
#ifndef CRYPTO
c->con_crypt_warning = FALSE;
#endif
conn = c;
bcopy(&from.sin_addr, &(c->con_addr),
sizeof(struct in_addr));
h = gethostbyaddr((char *) &from.sin_addr, sizeof(struct in_addr),
AF_INET);
if (h == NULL) {
strcpy(c->con_hostname, inet_ntoa(from.sin_addr));
} else {
strcpy(c->con_hostname, h->h_name);
}
}
} else if (c->con_timeout == -1) {
newconn = TRUE;
}
/* Initialise fields in connection. Only fields which need to
be reinitialised when a previously idle host resumes activity
need be set here. */
if (newconn) {
c->face_file = NULL;
c->face_filename[0] = 0;
c->face_viewer = 0;
c->face_stat = FSinit;
c->face_address = 0L;
c->face_retry = 0;
c->con_compmodes = -1;
c->con_protocol = PROTOCOL_UNKNOWN;
c->con_rseq = -1;
c->con_reply_current = FALSE;
c->con_busy = 0;
bcopy("\221\007\311\201", c->con_session, 4);
lpc_init(&c->lpcc);
busyreject = isBusy();
}
if (c != NULL) {
/* Reset connection timeout. */
c->con_timeout = 0;
/* If we're rejecting this with a busy signal, start
the busy timeout running and dispatch the busy
signal to the host. */
if (busyreject && c->con_busy == 0) {
pid_t cpid;
c->con_busy = busytimeout;
if (audiok) {
soundterm();
audiok = FALSE;
if (Debug) {
fprintf(stderr, "%s: releasing audio before busy signal fork().\n", prog);
}
}
cpid = fork();
if (cpid == 0) {
char s[256];
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
/* Now we need to close any shared resources
that might have been inherited from the parent
process to avoid their being locked up for the
duration of the child process execution. */
close(sock);
if (record != NULL) {
fclose(record);
}
if (facefile != NULL) {
fclose(facefile);
}
sprintf(s, busysignal, inet_ntoa(from.sin_addr));
if (Debug) {
fprintf(stderr, "%s: sending busy signal with %s\n",
prog, s);
}
system(s);
exit(0);
} else if (cpid == (pid_t) -1) {
perror("creating busy signal process");
}
}
if (newconn) {
if (showhosts) {
fprintf(stderr, "%s: %s %s %s\n", prog, etime(),
c->con_hostname,
busyreject ? "sending busy signal" : "connect");
}
}
if (busyreject) {
continue;
}
/* Request face data from the other end, starting with
block zero. If the connection was created itself
by a face data request, don't request the face from
the other end; wait, instead, for some sound to
arrive. We use face_stat to decide when to make the
request rather than newconn, since a connection may
have been created by a face request from the other
end, which didn't trigger a reciprocal request by
us. */
if (!control && (c->con_protocol == PROTOCOL_SPEAKFREE) &&
(c->face_stat == FSinit) &&
isSoundPacket(ntohl(sb.compression)) &&
(ntohl(sb.compression) & fFaceOffer)) {
c->face_address = 0;
c->face_timeout = 0;
c->face_retry = 0;
c->face_stat = FSreply; /* Activate request from timeout */
faceTransferActive++; /* Mark face transfer underway */
if (faceTransferActive == 1) {
windtimer(); /* Set timer to fast cadence */
}
}
} else {
continue;
}
/* If we've sent the connection a busy signal, ignore all
packets from it until the busy timeout expires. */
if (c->con_busy > 0) {
if (Debug) {
fprintf(stderr, "%s: busy--discarding packet from %s.\n", prog,
c->con_hostname);
}
continue;
}
wasrtp = FALSE;
#ifdef CRYPTO
/* If a DES key is present and we're talking RTP or VAT
protocol we must decrypt the packet at this point.
We decrypt the packet if:
1. A DES key was given on the command line, and
either:
a) The packet arrived on the control port
(and hence must be from an RTP/VAT client), or
b) The protocol has already been detected as
RTP or VAT by reception of a valid control
port message. */
if ((control || (c->con_protocol == PROTOCOL_RTP) ||
(c->con_protocol == PROTOCOL_VAT)) &&
rtpdeskey[0]) {
/* One more little twist. If this packet arrived on the
control channel, see if it passes all the tests for a
valid RTCP packet. If so, we'll assume it isn't
encrypted. RTP utilities have the option of either
encrypting their control packets or sending them in
the clear, so a hack like this is the only way we have
to guess whether something we receive is encrypted. */
if (!isValidRTCPpacket((unsigned char *) &sb, rll)) {
des_key_schedule sched;
des_cblock ivec;
int drll = rll;
char *whichkey;
static int toggle = 0;
bzero(ivec, 8);
drll = (rll + 7) & (~7);
if (Debug) {
fprintf(stderr, "Decrypting %d VAT/RTP bytes with DES key.\r\n",
drll);
}
if (drll > rll) {
/* Should only happen for VAT protocol. Zero the rest of
the DES encryption block to guarantee consistency. */
bzero(((char *) &sb) + rll, drll - rll);
}
/* If the protocol is unknown, toggle back and forth
between the RTP and VAT DES keys until we crack the
packet and set the protocol. */
if (c->con_protocol == PROTOCOL_UNKNOWN ||
c->con_protocol == PROTOCOL_VATRTP_CRYPT ||
c->con_protocol == PROTOCOL_SPEAKFREE) {
whichkey = toggle == 0 ? vatdeskey :
(toggle == 1 ? rtpdeskey : NULL);
toggle = (toggle + 1) % 3;
} else {
whichkey = c->con_protocol == PROTOCOL_VAT ?
vatdeskey : rtpdeskey;
}
if (whichkey != NULL) {
des_set_key((des_cblock *) (whichkey + 1), sched);
des_ncbc_encrypt((des_cblock *) &sb,
(des_cblock *) &sb, rll, sched,
(des_cblock *) ivec, DES_DECRYPT);
/* Just one more thing. In RTP (unlike VAT), when
an RTCP control packet is encrypted, 4 bytes of
random data are prefixed to the packet to prevent
known-plaintext attacks. We have to strip this
prefix after decrypting. */
if (control && ((*(((char *) &sb) + 4) & 0xC0) == 0x80)) {
rll -= 4;
bcopy(((char *) &sb) + 4, (char *) &sb, rll);
}
}
#ifdef HEXDUMP
if (hexdump) {
xd(stderr, &sb, rll, TRUE);
}
#endif
}
}
#endif
/* If this packet arrived on the session control port, dispatch
it to the appropriate handler for its protocol. */
if (control) {
short protocol = PROTOCOL_VATRTP_CRYPT;
unsigned char *p = (unsigned char *) &sb;
unsigned char *apkt;
int proto = (p[0] >> 6) & 3;
if (proto == 0) { /* VAT */
/* To avoid spoofing by bad encryption keys, require
a proper ID message be seen before we'll flip into
VAT protocol. */
if (((p[1] == 1) || (p[1] == 3)) ||
((c->con_protocol == PROTOCOL_VAT) && (p[1] == 2))) {
protocol = PROTOCOL_VAT;
bcopy(p + 2, c->con_session, 2); /* Save conference ID */
if (p[1] == 1 && showhosts) {
char uname[256];
bcopy(p + 4, uname, rll - 4);
uname[rll - 4] = 0;
if (strcmp(uname, c->con_uname) != 0) {
strcpy(c->con_uname, uname);
fprintf(stderr, "%s: %s sending from %s.\n", prog,
c->con_uname, c->con_hostname);
}
}
/* Handling of VAT IDLIST could be a lot more elegant
than this. */
if (p[1] == 3 && showhosts) {
char *uname;
uname = (char *) malloc(rll);
if (uname != NULL) {
unsigned char *bp = p, *ep = p + rll;
int i = bp[4];
bp += 8;
uname[0] = 0;
*ep = 0;
while (--i >= 0 && bp < ep) {
bp += 4;
strcat(uname, "\t");
strcat(uname, (char *) bp);
while (isspace(uname[strlen(uname) - 1])) {
uname[strlen(uname) - 1] = 0;
}
strcat(uname, "\n");
bp += (strlen((char *) bp) + 3) & ~3;
}
if (strncmp(uname, c->con_uname, (sizeof c->con_uname - 1)) != 0) {
strncpy(c->con_uname, uname, sizeof c->con_uname);
if (strlen(uname) > ((sizeof c->con_uname) - 1)) {
c->con_uname[((sizeof c->con_uname) - 1)] = 0;
}
fprintf(stderr, "%s: now in conference at %s:\n%s", prog,
c->con_hostname, uname);
}
free(uname);
}
}
/* If it's a DONE packet, reset protocol to unknown. */
if (p[1] == 2) {
c->con_protocol = protocol = PROTOCOL_UNKNOWN;
c->con_timeout = hosttimeout - 1;
c->con_uname[0] = c->con_email[0] = 0;
if (showhosts) {
fprintf(stderr, "%s: %s VAT connection closed.\n",
prog, c->con_hostname);
}
}
}
} else if (proto == RTP_VERSION || proto == 1) { /* RTP */
if (isValidRTCPpacket((unsigned char *) &sb, rll)) {
protocol = (proto == 1) ? PROTOCOL_SPEAKFREE : PROTOCOL_RTP;
bcopy(p + 4, c->con_session, 4); /* Save SSRC */
/* If it's a BYE packet, reset protocol to unknown. */
if (isRTCPByepacket((unsigned char *) &sb, rll)) {
c->con_protocol = protocol = PROTOCOL_UNKNOWN;
c->con_timeout = hosttimeout - 1;
c->con_uname[0] = c->con_email[0] = 0;
if (showhosts) {
fprintf(stderr, "%s: %s %s connection closed.\n",
prog, c->con_hostname,
proto == 1 ? "Speak Freely" : "RTP");
}
/* If it's a text chat message, print it. */
} else if (isRTCPAPPpacket((unsigned char *) &sb, rll,
RTCP_APP_TEXT_CHAT, &apkt) && apkt != NULL) {
char *ident = c->con_hostname;
/* To identify the sender, get successively more
personal depending on the information we have at
hand, working down from hostname (which may just
be an IP address if we couldn't resolve the host,
through E-mail address, to user name. */
if (c->con_email[0] != 0) {
ident = c->con_email;
}
if (c->con_uname[0] != 0) {
ident = ident = c->con_uname;
}
printf("%s: %s\n", ident, (char *) (apkt + 12));
/* Otherwise, it's presumably an SDES, from which we
should update the user identity information for the
connection. */
} else {
struct rtcp_sdes_request rp;
rp.nitems = 4;
rp.item[0].r_item = RTCP_SDES_CNAME;
rp.item[1].r_item = RTCP_SDES_NAME;
rp.item[2].r_item = RTCP_SDES_EMAIL;
rp.item[3].r_item = RTCP_SDES_TOOL;
if (parseSDES((unsigned char *) &sb, &rp)) {
char uname[256], email[256];
uname[0] = email[0] = 0;
if (rp.item[1].r_text != NULL) {
copySDESitem(rp.item[1].r_text, uname);
if (rp.item[2].r_text != NULL) {
copySDESitem(rp.item[2].r_text, email);
} else if (rp.item[2].r_text != NULL) {
copySDESitem(rp.item[0].r_text, email);
}
} else if (rp.item[2].r_text != NULL) {
copySDESitem(rp.item[2].r_text, uname);
} else if (rp.item[0].r_text != NULL) {
copySDESitem(rp.item[0].r_text, uname);
}
if (strcmp(uname, c->con_uname) != 0 ||
strcmp(email, c->con_email) != 0) {
strcpy(c->con_uname, uname);
strcpy(c->con_email, email);
if (showhosts && uname[0]) {
fprintf(stderr, "%s: %s", prog, uname);
if (email[0]) {
fprintf(stderr, " (%s)", email);
}
fprintf(stderr, " sending from %s.\n",
c->con_hostname);
}
}
}
}
} else {
if (Debug) {
fprintf(stderr, "Invalid RTCP packet received.\n");
}
}
} else {
if (Debug) {
fprintf(stderr, "Bogus protocol 3 control message.\n");
}
}
/* If protocol changed, update in connection and, if appropriate,
update the reply command. */
if (protocol != c->con_protocol) {
static char *pname[] = {
"Speak Freely",
"VAT",
"RTP",
"VAT/RTP encrypted",
"Unknown"
};
c->con_protocol = protocol;
if (showhosts) {
fprintf(stderr, "%s: %s sending in %s protocol.\n",
prog, c->con_hostname, pname[protocol]);
}
c->con_reply_current = FALSE;
}
continue;
}
/* If this message is tagged with our Speak Freely protocol
bit, force protocol back to Speak Freely. This allows us
to switch back to Speak Freely after receiving packets in
VAT. We can still get confused if we receive a packet from
an older version of Speak Freely that doesn't tag. */
if (c->con_protocol == PROTOCOL_VAT ||
c->con_protocol == PROTOCOL_VATRTP_CRYPT) {
unsigned char *p = (unsigned char *) &sb;
if (((p[0] >> 6) & 3) == 1) {
c->con_protocol = PROTOCOL_SPEAKFREE;
}
}
/* If this is a VAT packet, translate it into a sound buffer. */
if (((c->con_protocol == PROTOCOL_VAT)) &&
(bcmp(((unsigned char *) &sb) + 2, c->con_session, 2) == 0) &&
isvat((unsigned char *) &sb, rll)) {
if (sb.buffer.buffer_len == 0) {
if (Debug) {
fprintf(stderr, "Ignoring unparseable VAT packet.\n");
}
continue;
}
wasrtp = TRUE;
/* If this is an RTP packet, transmogrify it into a sound
buffer we can understand. */
} else if ((c->con_protocol == PROTOCOL_RTP) &&
(bcmp(((unsigned char *) &sb) + 8, c->con_session, 4) == 0) &&
isrtp((unsigned char *) &sb, rll)) {
if (sb.buffer.buffer_len == 0) {
if (Debug) {
fprintf(stderr, "Ignoring unparseable RTP packet.\n");
}
continue;
}
wasrtp = TRUE;
}
if (!wasrtp) {
int xbl;
/* Convert relevant fields from network to host
byte order, if necessary. */
sb.compression = ntohl(sb.compression);
sb.buffer.buffer_len = ntohl(sb.buffer.buffer_len);
if (sb.compression & fCompRobust) {
int aseq = (sb.buffer.buffer_len >> 24) & 0xFF;
if (aseq == c->con_rseq) {
continue;
}
c->con_rseq = aseq;
sb.buffer.buffer_len &= 0xFFFFFFL;
}
/* Now if this is a valid Speak Freely packet (as
opposed to a VAT packet masquerading as one, or
an encrypted VAT or RTP packet we don't have the
proper key to decode), the length received from the
socket will exactly equal the buffer length plus
the size of the header. This is a reasonably
good validity check, well worth it considering the
horrors treating undecipherable garbage as a sound
buffer could inflict on us. */
xbl = sb.buffer.buffer_len + (sizeof(struct soundbuf) - BUFL);
/* If this packet is encrypted with an algorithm which requires
padding the packet to an 8-byte boundary, adjust the actual
content length to account for the padding. */
if ((sb.compression & (fEncDES | fEncIDEA | fEncBF | fEncPGP)) != 0) {
xbl = ((sb.buffer.buffer_len + 7) & (~7)) +
(sizeof(struct soundbuf) - BUFL);
}
/* If packet is compressed with LPC-10, compensate for "packet
stuffing". */
if ((sb.compression & fCompLPC10) && (sb.buffer.buffer_len >= 16)) {
xbl -= 16;
}
if (xbl != rll) {
if (Debug) {
fprintf(stderr,
"Sound buffer length %ld doesn't match %ld byte packet. Hdr=%08X\n",
xbl, rll, sb.compression);
}
if (showhosts && c->con_protocol != PROTOCOL_UNKNOWN) {
fprintf(stderr, "%s: %s sending in unknown protocol or encryption.\n",
prog, c->con_hostname);
}
c->con_protocol = PROTOCOL_UNKNOWN;
continue;
}
/* It does appear to be a genuine Speak Freely sound
buffer. On that basis, set the protocol to Speak Freely
even if the buffer isn't explicitly tagged. */
if (c->con_protocol != PROTOCOL_SPEAKFREE) {
c->con_protocol = PROTOCOL_SPEAKFREE;
if (showhosts) {
fprintf(stderr, "%s: %s sending in Speak Freely protocol.\n", prog, c->con_hostname);
}
c->con_reply_current = FALSE;
}
}
if ((replycmdexe != NULL && !c->con_reply_current)) {
char *rcmd = StrReplace(replycmdexe, "%s", c->con_hostname);
if (rcmd != NULL) {
if (Debug) {
fprintf(stderr, "Executing: %s\n", rcmd);
}
system(rcmd);
c->con_reply_current = TRUE;
if (rcmd != replycmdexe) {
free(rcmd);
}
}
}
/* If this is the first sound packet we've seen from this host
in this protocol, create (or update) the reply command file. */
if (replyfile != NULL && !c->con_reply_current) {
/* If a reply file is requested, create an executable
shell script to reply to the host that just connected
using either the default command or whatever command
the user specifies on the -A option. */
FILE *rfp = fopen(replyfile, "w");
static char *popt[] = {
"",
"-vat ",
"-rtp ",
"-rtp ",
""
};
if (rfp != NULL) {
fprintf(rfp, "#! /bin/sh\n");
/* $* allows specifying options on reply file call. */
fprintf(rfp, "%s %s$* %s\n", replycmd,
popt[c->con_protocol], c->con_hostname);
/* Make the file executable. */
fchmod(fileno(rfp), 0755);
fclose(rfp);
}
c->con_reply_current = TRUE;
}
#ifdef HALF_DUPLEX
/* If this is a half duplex mute request, immediately release
the audio output device if we have it. We verify the
sender's address and accept mute requests only frome
localhost; nobody else has any business telling us
to shut up! */
if (sb.compression & fHalfDuplex) {
#ifdef HDX_DEBUG
if (Debug) {
static char *hdxreq[4] = { "Bogus(0)", "Mute", "Resume",
"Bogus(3)" };
fprintf(stderr, "%s: half-duplex %s request from %s.\n",
prog, hdxreq[sb.compression & 3], inet_ntoa(from.sin_addr));
}
#endif
if (memcmp(&from.sin_addr, &localhost, sizeof localhost) == 0) {
if (sb.compression & fHalfDuplexMute) {
if (audiok) {
soundterm();
audiok = FALSE;
if (Debug) {
fprintf(stderr, "%s: half-duplex releasing audio output.\n", prog);
}
}
halfDuplexMuted = TRUE;
} else if (sb.compression & fHalfDuplexResume) {
halfDuplexMuted = FALSE;
}
}
continue; /* Done with packet */
}
#endif
/* If this is a face request and we have a face file open,
respond to it. Note that servicing of face file data requests
is stateless. */
if (sb.compression & fFaceData) {
if (sb.compression & faceRequest) {
long l;
/* Request for face data. */
if (facefile != NULL) {
fseek(facefile, sb.buffer.buffer_len, 0);
*((long *) sb.buffer.buffer_val) = htonl(sb.buffer.buffer_len);
l = fread(sb.buffer.buffer_val + sizeof(long),
1, 512 - (sizeof(long) + (sizeof(soundbuf) - BUFL)), facefile);
sb.compression = fProtocol | fFaceData | faceReply;
if (Debug) {
fprintf(stderr, "%s: sending %d bytes of face data at %ld to %s\n",
prog, l, ntohl(*((long *) sb.buffer.buffer_val)), c->con_hostname);
}
l += sizeof(long);
} else {
/* No face file. Shut down requestor. */
sb.compression = fProtocol | fFaceData | faceLess;
l = 0;
}
bcopy((char *) &(from.sin_addr), (char *) &(name.sin_addr),
sizeof(struct in_addr));
sb.compression = htonl(sb.compression);
sb.buffer.buffer_len = htonl(l);
if (sendto(sock, (char *) &sb,
(int) ((sizeof(struct soundbuf) - BUFL) + l),
0, (struct sockaddr *) &(name), sizeof name) < 0) {
perror("sending face image data");
}
} else if (sb.compression & faceReply) {
/* Face data packet received from remote server. */
if ((c->face_file == NULL) && (sb.buffer.buffer_len > 0)) {
sprintf(c->face_filename, "%sSF-%s.bmp", FACEDIR, c->con_hostname);
c->face_file = fopen(c->face_filename, "w");
}
if (c->face_file != NULL) {
if (sb.buffer.buffer_len > sizeof(long)) {
long lp = ntohl(*((long *) sb.buffer.buffer_val));
if (lp == c->face_address) {
fseek(c->face_file, lp, 0);
fwrite(sb.buffer.buffer_val + sizeof(long),
sb.buffer.buffer_len - sizeof(long), 1,
c->face_file);
if (Debug) {
fprintf(stderr, "%s: writing %d bytes at %ld in face file %s\n",
prog, sb.buffer.buffer_len - sizeof(long),
lp, c->face_filename);
}
c->face_address += sb.buffer.buffer_len - sizeof(long);
/* Timeout will make next request after the
configured interval. */
c->face_stat = FSreply;
c->face_retry = 0;
} else {
if (Debug) {
fprintf(stderr, "%s: discarded %d bytes for %ld in face file %s, expected data for %ld\n",
prog, sb.buffer.buffer_len - sizeof(long),
lp, c->face_filename, c->face_address);
}
}
} else {
pid_t cpid;
if (Debug) {
fprintf(stderr, "%s: closing face file %s\n",
prog, c->face_filename);
}
fclose(c->face_file);
c->face_file = NULL;
c->face_stat = FScomplete;
faceTransferActive--;
/* Start viewer to display face. We terminate
audio output (if active) before doing this since
we don't know the nature of the audio output
resource. If it's an open file handle which
would be inherited by the child process, that
would hang the audio device as long as the
viewer is active. */
if (audiok) {
soundterm();
audiok = FALSE;
if (Debug) {
fprintf(stderr, "%s: releasing audio before viewer fork().\n", prog);
}
}
cpid = fork();
if (cpid == 0) {
char geom[30], *gp1 = NULL, *gp2 = NULL;
#ifdef NEEDED
/* These should be reset by the execlp(). */
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
signal(SIGALRM, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
#endif
/* Now we need to close any shared resources
that might have been inherited from the parent
process to avoid their being locked up for the
duration of the viewer's execution. */
close(sock);
if (record != NULL) {
fclose(record);
}
if (facefile != NULL) {
fclose(facefile);
}
#ifdef FACE_SET_GEOMETRY
/* Attempt to reasonably place successive face windows
on the screen to avoid the user's having to place
them individually (for window managers with
interactivePlacement enabled). */
#define faceInterval 120 /* Interval, in pixels, between successive faces */
sprintf(geom, "-0+%d", facesDisplayed * faceInterval);
gp1 = "-geometry";
gp2 = geom;
#endif
execlp("xv", "xv", c->face_filename, gp1, gp2, (char *) 0);
perror("launching face image viewer");
facesDisplayed--;
exit(0);
/* Leave face image around, for the moment, so the user can
try to view it manually. */
} else if (cpid == (pid_t) -1) {
perror("creating face image viewer process");
} else {
c->face_viewer = cpid;
facesDisplayed++;
}
}
}
} else if (sb.compression & faceLess) {
if (c->face_file != NULL) {
fclose(c->face_file);
unlink(c->face_filename);
}
c->face_stat = FSabandoned;
faceTransferActive--;
if (Debug) {
fprintf(stderr, "%s: no face image available for %s\n",
prog, c->con_hostname);
}
}
continue; /* Done with packet */
}
/* If the packet requests loop-back, immediately dispatch it
back to the host who sent it to us. To prevent an infinite
loop-back cycle, we clear the loop-back bit in the header
before sending the message. We leave the host of origin
unchanged, allowing the sender to identify the packet as
one he originated. */
if (sb.compression & fLoopBack) {
bcopy((char *) &(from.sin_addr), (char *) &(name.sin_addr),
sizeof(struct in_addr));
sb.compression &= ~fLoopBack; /* Prevent infinite loopback */
sb.compression = htonl(sb.compression);
sb.buffer.buffer_len = htonl(sb.buffer.buffer_len);
if (sendto(sock, (char *) &sb, rll,
0, (struct sockaddr *) &(name), sizeof name) < 0) {
perror("sending datagram message");
}
sb.compression = ntohl(sb.compression);
sb.buffer.buffer_len = ntohl(sb.buffer.buffer_len);
}
#ifdef HALF_DUPLEX
/* If we're muted by a transmission in progress on half-duplex
audio hardware, this is the end of line for this sound
packet. */
if (halfDuplexMuted) {
if (Debug) {
fprintf(stderr, "%s: %s packet lost by half-duplex muting.\n",
prog, c->con_hostname);
}
continue;
}
#endif
/* If this packet has been "stuffed" for maximum efficiency,
un-stuff it at this point. */
if ((sb.compression & fCompLPC10) && (sb.buffer.buffer_len >= 16)) {
bcopy(sb.sendinghost, (char *) &sb + rll,
sizeof sb.sendinghost);
rll += sizeof sb.sendinghost;
}
#ifdef CRYPTO
if ((sb.compression & fKeyPGP)) {
char cmd[256], f[40], kmd[16];
FILE *kfile;
FILE *pipe;
struct MD5Context md5c;
MD5Init(&md5c);
MD5Update(&md5c, sb.buffer.buffer_val, sb.buffer.buffer_len);
MD5Final(kmd, &md5c);
if (memcmp(c->keymd5, kmd, 16) != 0) {
bcopy(kmd, c->keymd5, 16);
sprintf(f, "/tmp/.SF_SKEY%d", getpid());
kfile = fopen(f, "w");
if (kfile == NULL) {
fprintf(stderr, "Cannot open encrypted session key file %s\n", f);
} else {
fwrite(sb.buffer.buffer_val, sb.buffer.buffer_len, 1, kfile);
fclose(kfile);
#ifdef ZZZ
if (pgppass == NULL) {
static char s[256];
fprintf(stderr, "Enter PGP pass phrase: ");
if (fgets(s, sizeof s, stdin) != NULL) {
s[strlen(s) - 1] = 0;
pgppass = s;
}
}
#endif
sprintf(cmd, "pgp -f +nomanual +verbose=0 +armor=off %s%s%s <%s",
pgppass ? "-z\"" : "", pgppass ? pgppass : "",
pgppass ? "\" " : "", f);
#ifdef PGP_DEBUG
fprintf(stderr, "Decoding session key with: %s\n", cmd);
#else
if (Debug) {
fprintf(stderr, "%s: decoding PGP session key.\n", prog);
}
#endif
pipe = popen(cmd, "r");
if (pipe == NULL) {
fprintf(stderr, "Unable to open pipe to: %s\n", cmd);
} else {
int lr;
/* Okay, explanation time again. On some systems
(Silicon Graphics, for example), the timer tick
alarm signal can cause the pending read from the
PGP key pipe to return an "Interrupted system
call" status (EINTR) with (as far as I've ever
seen and I sincerely hope it's always) zero bytes
read. This happens frequently when the timer is
running and the user takes longer to enter the
secret key pass phrase than the timer tick. So,
if this happens we keep on re-issuing the pipe
read until the phrase allows PGP to finish the
job. */
while ((lr = fread(c->pgpkey, 1, 17, pipe)) != 17 &&
(errno == EINTR)) ;
if (lr == 17) {
c->pgpkey[0] = TRUE;
#ifdef PGP_DEBUG
{
int i;
fprintf(stderr, "Session key for %s:", c->con_hostname);
for (i = 0; i < 16; i++) {
fprintf(stderr, " %02X", c->pgpkey[i + 1] & 0xFF);
}
fprintf(stderr, "\n");
}
#else
if (Debug) {
fprintf(stderr, "%s: PGP session key decoded.\n", prog);
}
#endif
} else {
c->pgpkey[0] = FALSE;
fprintf(stderr, "%s: Error decoding PGP session key.\n", prog);
#ifdef PGP_DEBUG
fprintf(stderr, "Read status from pipe: %d\n", lr);
perror("reading decoded PGP key from pipe");
#endif
}
pclose(pipe);
}
unlink(f);
}
}
} else
#endif
{
playbuffer(&sb, c);
}
}
#ifdef MEANS_OF_EXIT
close(sock);
#ifdef CRYPTO
desdone();
#endif
gsm_destroy(gsmh);
lpc_end();
exiting();
if (record != NULL) {
fclose(record);
}
if (facefile != NULL) {
fclose(facefile);
}
return 0;
#endif
}